/*!
 * vue-router v0.7.13
 * (c) 2016 Evan You
 * Released under the MIT License.
 */
(function (global, factory) {
    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
        typeof define === 'function' && define.amd ? define(factory) :
            global.VueRouter = factory();
}(this, function () { 'use strict';

    var babelHelpers = {};

    babelHelpers.classCallCheck = function (instance, Constructor) {
        if (!(instance instanceof Constructor)) {
            throw new TypeError("Cannot call a class as a function");
        }
    };
    function Target(path, matcher, delegate) {
        this.path = path;
        this.matcher = matcher;
        this.delegate = delegate;
    }

    Target.prototype = {
        to: function to(target, callback) {
            var delegate = this.delegate;

            if (delegate && delegate.willAddRoute) {
                target = delegate.willAddRoute(this.matcher.target, target);
            }

            this.matcher.add(this.path, target);

            if (callback) {
                if (callback.length === 0) {
                    throw new Error("You must have an argument in the function passed to `to`");
                }
                this.matcher.addChild(this.path, target, callback, this.delegate);
            }
            return this;
        }
    };

    function Matcher(target) {
        this.routes = {};
        this.children = {};
        this.target = target;
    }

    Matcher.prototype = {
        add: function add(path, handler) {
            this.routes[path] = handler;
        },

        addChild: function addChild(path, target, callback, delegate) {
            var matcher = new Matcher(target);
            this.children[path] = matcher;

            var match = generateMatch(path, matcher, delegate);

            if (delegate && delegate.contextEntered) {
                delegate.contextEntered(target, match);
            }

            callback(match);
        }
    };

    function generateMatch(startingPath, matcher, delegate) {
        return function (path, nestedCallback) {
            var fullPath = startingPath + path;

            if (nestedCallback) {
                nestedCallback(generateMatch(fullPath, matcher, delegate));
            } else {
                return new Target(startingPath + path, matcher, delegate);
            }
        };
    }

    function addRoute(routeArray, path, handler) {
        var len = 0;
        for (var i = 0, l = routeArray.length; i < l; i++) {
            len += routeArray[i].path.length;
        }

        path = path.substr(len);
        var route = { path: path, handler: handler };
        routeArray.push(route);
    }

    function eachRoute(baseRoute, matcher, callback, binding) {
        var routes = matcher.routes;

        for (var path in routes) {
            if (routes.hasOwnProperty(path)) {
                var routeArray = baseRoute.slice();
                addRoute(routeArray, path, routes[path]);

                if (matcher.children[path]) {
                    eachRoute(routeArray, matcher.children[path], callback, binding);
                } else {
                    callback.call(binding, routeArray);
                }
            }
        }
    }

    function map (callback, addRouteCallback) {
        var matcher = new Matcher();

        callback(generateMatch("", matcher, this.delegate));

        eachRoute([], matcher, function (route) {
            if (addRouteCallback) {
                addRouteCallback(this, route);
            } else {
                this.add(route);
            }
        }, this);
    }

    var specials = ['/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\'];

    var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g');

    var noWarning = false;
    function warn(msg) {
        if (!noWarning && typeof console !== 'undefined') {
            console.error('[vue-router] ' + msg);
        }
    }

    function tryDecode(uri, asComponent) {
        try {
            return asComponent ? decodeURIComponent(uri) : decodeURI(uri);
        } catch (e) {
            warn('malformed URI' + (asComponent ? ' component: ' : ': ') + uri);
        }
    }

    function isArray(test) {
        return Object.prototype.toString.call(test) === "[object Array]";
    }

    // A Segment represents a segment in the original route description.
    // Each Segment type provides an `eachChar` and `regex` method.
    //
    // The `eachChar` method invokes the callback with one or more character
    // specifications. A character specification consumes one or more input
    // characters.
    //
    // The `regex` method returns a regex fragment for the segment. If the
    // segment is a dynamic of star segment, the regex fragment also includes
    // a capture.
    //
    // A character specification contains:
    //
    // * `validChars`: a String with a list of all valid characters, or
    // * `invalidChars`: a String with a list of all invalid characters
    // * `repeat`: true if the character specification can repeat

    function StaticSegment(string) {
        this.string = string;
    }
    StaticSegment.prototype = {
        eachChar: function eachChar(callback) {
            var string = this.string,
                ch;

            for (var i = 0, l = string.length; i < l; i++) {
                ch = string.charAt(i);
                callback({ validChars: ch });
            }
        },

        regex: function regex() {
            return this.string.replace(escapeRegex, '\\$1');
        },

        generate: function generate() {
            return this.string;
        }
    };

    function DynamicSegment(name) {
        this.name = name;
    }
    DynamicSegment.prototype = {
        eachChar: function eachChar(callback) {
            callback({ invalidChars: "/", repeat: true });
        },

        regex: function regex() {
            return "([^/]+)";
        },

        generate: function generate(params) {
            var val = params[this.name];
            return val == null ? ":" + this.name : val;
        }
    };

    function StarSegment(name) {
        this.name = name;
    }
    StarSegment.prototype = {
        eachChar: function eachChar(callback) {
            callback({ invalidChars: "", repeat: true });
        },

        regex: function regex() {
            return "(.+)";
        },

        generate: function generate(params) {
            var val = params[this.name];
            return val == null ? ":" + this.name : val;
        }
    };

    function EpsilonSegment() {}
    EpsilonSegment.prototype = {
        eachChar: function eachChar() {},
        regex: function regex() {
            return "";
        },
        generate: function generate() {
            return "";
        }
    };

    function parse(route, names, specificity) {
        // normalize route as not starting with a "/". Recognition will
        // also normalize.
        if (route.charAt(0) === "/") {
            route = route.substr(1);
        }

        var segments = route.split("/"),
            results = [];

        // A routes has specificity determined by the order that its different segments
        // appear in. This system mirrors how the magnitude of numbers written as strings
        // works.
        // Consider a number written as: "abc". An example would be "200". Any other number written
        // "xyz" will be smaller than "abc" so long as `a > z`. For instance, "199" is smaller
        // then "200", even though "y" and "z" (which are both 9) are larger than "0" (the value
        // of (`b` and `c`). This is because the leading symbol, "2", is larger than the other
        // leading symbol, "1".
        // The rule is that symbols to the left carry more weight than symbols to the right
        // when a number is written out as a string. In the above strings, the leading digit
        // represents how many 100's are in the number, and it carries more weight than the middle
        // number which represents how many 10's are in the number.
        // This system of number magnitude works well for route specificity, too. A route written as
        // `a/b/c` will be more specific than `x/y/z` as long as `a` is more specific than
        // `x`, irrespective of the other parts.
        // Because of this similarity, we assign each type of segment a number value written as a
        // string. We can find the specificity of compound routes by concatenating these strings
        // together, from left to right. After we have looped through all of the segments,
        // we convert the string to a number.
        specificity.val = '';

        for (var i = 0, l = segments.length; i < l; i++) {
            var segment = segments[i],
                match;

            if (match = segment.match(/^:([^\/]+)$/)) {
                results.push(new DynamicSegment(match[1]));
                names.push(match[1]);
                specificity.val += '3';
            } else if (match = segment.match(/^\*([^\/]+)$/)) {
                results.push(new StarSegment(match[1]));
                specificity.val += '2';
                names.push(match[1]);
            } else if (segment === "") {
                results.push(new EpsilonSegment());
                specificity.val += '1';
            } else {
                results.push(new StaticSegment(segment));
                specificity.val += '4';
            }
        }

        specificity.val = +specificity.val;

        return results;
    }

    // A State has a character specification and (`charSpec`) and a list of possible
    // subsequent states (`nextStates`).
    //
    // If a State is an accepting state, it will also have several additional
    // properties:
    //
    // * `regex`: A regular expression that is used to extract parameters from paths
    //   that reached this accepting state.
    // * `handlers`: Information on how to convert the list of captures into calls
    //   to registered handlers with the specified parameters
    // * `types`: How many static, dynamic or star segments in this route. Used to
    //   decide which route to use if multiple registered routes match a path.
    //
    // Currently, State is implemented naively by looping over `nextStates` and
    // comparing a character specification against a character. A more efficient
    // implementation would use a hash of keys pointing at one or more next states.

    function State(charSpec) {
        this.charSpec = charSpec;
        this.nextStates = [];
    }

    State.prototype = {
        get: function get(charSpec) {
            var nextStates = this.nextStates;

            for (var i = 0, l = nextStates.length; i < l; i++) {
                var child = nextStates[i];

                var isEqual = child.charSpec.validChars === charSpec.validChars;
                isEqual = isEqual && child.charSpec.invalidChars === charSpec.invalidChars;

                if (isEqual) {
                    return child;
                }
            }
        },

        put: function put(charSpec) {
            var state;

            // If the character specification already exists in a child of the current
            // state, just return that state.
            if (state = this.get(charSpec)) {
                return state;
            }

            // Make a new state for the character spec
            state = new State(charSpec);

            // Insert the new state as a child of the current state
            this.nextStates.push(state);

            // If this character specification repeats, insert the new state as a child
            // of itself. Note that this will not trigger an infinite loop because each
            // transition during recognition consumes a character.
            if (charSpec.repeat) {
                state.nextStates.push(state);
            }

            // Return the new state
            return state;
        },

        // Find a list of child states matching the next character
        match: function match(ch) {
            // DEBUG "Processing `" + ch + "`:"
            var nextStates = this.nextStates,
                child,
                charSpec,
                chars;

            // DEBUG "  " + debugState(this)
            var returned = [];

            for (var i = 0, l = nextStates.length; i < l; i++) {
                child = nextStates[i];

                charSpec = child.charSpec;

                if (typeof (chars = charSpec.validChars) !== 'undefined') {
                    if (chars.indexOf(ch) !== -1) {
                        returned.push(child);
                    }
                } else if (typeof (chars = charSpec.invalidChars) !== 'undefined') {
                    if (chars.indexOf(ch) === -1) {
                        returned.push(child);
                    }
                }
            }

            return returned;
        }

        /** IF DEBUG
         , debug: function() {
      var charSpec = this.charSpec,
          debug = "[",
          chars = charSpec.validChars || charSpec.invalidChars;
       if (charSpec.invalidChars) { debug += "^"; }
      debug += chars;
      debug += "]";
       if (charSpec.repeat) { debug += "+"; }
       return debug;
    }
         END IF **/
    };

    /** IF DEBUG
     function debug(log) {
    console.log(log);
  }

     function debugState(state) {
    return state.nextStates.map(function(n) {
      if (n.nextStates.length === 0) { return "( " + n.debug() + " [accepting] )"; }
      return "( " + n.debug() + " <then> " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )";
    }).join(", ")
  }
     END IF **/

    // Sort the routes by specificity
    function sortSolutions(states) {
        return states.sort(function (a, b) {
            return b.specificity.val - a.specificity.val;
        });
    }

    function recognizeChar(states, ch) {
        var nextStates = [];

        for (var i = 0, l = states.length; i < l; i++) {
            var state = states[i];

            nextStates = nextStates.concat(state.match(ch));
        }

        return nextStates;
    }

    var oCreate = Object.create || function (proto) {
            function F() {}
            F.prototype = proto;
            return new F();
        };

    function RecognizeResults(queryParams) {
        this.queryParams = queryParams || {};
    }
    RecognizeResults.prototype = oCreate({
        splice: Array.prototype.splice,
        slice: Array.prototype.slice,
        push: Array.prototype.push,
        length: 0,
        queryParams: null
    });

    function findHandler(state, path, queryParams) {
        var handlers = state.handlers,
            regex = state.regex;
        var captures = path.match(regex),
            currentCapture = 1;
        var result = new RecognizeResults(queryParams);

        for (var i = 0, l = handlers.length; i < l; i++) {
            var handler = handlers[i],
                names = handler.names,
                params = {};

            for (var j = 0, m = names.length; j < m; j++) {
                params[names[j]] = captures[currentCapture++];
            }

            result.push({ handler: handler.handler, params: params, isDynamic: !!names.length });
        }

        return result;
    }

    function addSegment(currentState, segment) {
        segment.eachChar(function (ch) {
            var state;

            currentState = currentState.put(ch);
        });

        return currentState;
    }

    function decodeQueryParamPart(part) {
        // http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.1
        part = part.replace(/\+/gm, '%20');
        return tryDecode(part, true);
    }

    // The main interface

    var RouteRecognizer = function RouteRecognizer() {
        this.rootState = new State();
        this.names = {};
    };

    RouteRecognizer.prototype = {
        add: function add(routes, options) {
            var currentState = this.rootState,
                regex = "^",
                specificity = {},
                handlers = [],
                allSegments = [],
                name;

            var isEmpty = true;

            for (var i = 0, l = routes.length; i < l; i++) {
                var route = routes[i],
                    names = [];

                var segments = parse(route.path, names, specificity);

                allSegments = allSegments.concat(segments);

                for (var j = 0, m = segments.length; j < m; j++) {
                    var segment = segments[j];

                    if (segment instanceof EpsilonSegment) {
                        continue;
                    }

                    isEmpty = false;

                    // Add a "/" for the new segment
                    currentState = currentState.put({ validChars: "/" });
                    regex += "/";

                    // Add a representation of the segment to the NFA and regex
                    currentState = addSegment(currentState, segment);
                    regex += segment.regex();
                }

                var handler = { handler: route.handler, names: names };
                handlers.push(handler);
            }

            if (isEmpty) {
                currentState = currentState.put({ validChars: "/" });
                regex += "/";
            }

            currentState.handlers = handlers;
            currentState.regex = new RegExp(regex + "$");
            currentState.specificity = specificity;

            if (name = options && options.as) {
                this.names[name] = {
                    segments: allSegments,
                    handlers: handlers
                };
            }
        },

        handlersFor: function handlersFor(name) {
            var route = this.names[name],
                result = [];
            if (!route) {
                throw new Error("There is no route named " + name);
            }

            for (var i = 0, l = route.handlers.length; i < l; i++) {
                result.push(route.handlers[i]);
            }

            return result;
        },

        hasRoute: function hasRoute(name) {
            return !!this.names[name];
        },

        generate: function generate(name, params) {
            var route = this.names[name],
                output = "";
            if (!route) {
                throw new Error("There is no route named " + name);
            }

            var segments = route.segments;

            for (var i = 0, l = segments.length; i < l; i++) {
                var segment = segments[i];

                if (segment instanceof EpsilonSegment) {
                    continue;
                }

                output += "/";
                output += segment.generate(params);
            }

            if (output.charAt(0) !== '/') {
                output = '/' + output;
            }

            if (params && params.queryParams) {
                output += this.generateQueryString(params.queryParams);
            }

            return output;
        },

        generateQueryString: function generateQueryString(params) {
            var pairs = [];
            var keys = [];
            for (var key in params) {
                if (params.hasOwnProperty(key)) {
                    keys.push(key);
                }
            }
            keys.sort();
            for (var i = 0, len = keys.length; i < len; i++) {
                key = keys[i];
                var value = params[key];
                if (value == null) {
                    continue;
                }
                var pair = encodeURIComponent(key);
                if (isArray(value)) {
                    for (var j = 0, l = value.length; j < l; j++) {
                        var arrayPair = key + '[]' + '=' + encodeURIComponent(value[j]);
                        pairs.push(arrayPair);
                    }
                } else {
                    pair += "=" + encodeURIComponent(value);
                    pairs.push(pair);
                }
            }

            if (pairs.length === 0) {
                return '';
            }

            return "?" + pairs.join("&");
        },

        parseQueryString: function parseQueryString(queryString) {
            var pairs = queryString.split("&"),
                queryParams = {};
            for (var i = 0; i < pairs.length; i++) {
                var pair = pairs[i].split('='),
                    key = decodeQueryParamPart(pair[0]),
                    keyLength = key.length,
                    isArray = false,
                    value;
                if (pair.length === 1) {
                    value = 'true';
                } else {
                    //Handle arrays
                    if (keyLength > 2 && key.slice(keyLength - 2) === '[]') {
                        isArray = true;
                        key = key.slice(0, keyLength - 2);
                        if (!queryParams[key]) {
                            queryParams[key] = [];
                        }
                    }
                    value = pair[1] ? decodeQueryParamPart(pair[1]) : '';
                }
                if (isArray) {
                    queryParams[key].push(value);
                } else {
                    queryParams[key] = value;
                }
            }
            return queryParams;
        },

        recognize: function recognize(path, silent) {
            noWarning = silent;
            var states = [this.rootState],
                pathLen,
                i,
                l,
                queryStart,
                queryParams = {},
                isSlashDropped = false;

            queryStart = path.indexOf('?');
            if (queryStart !== -1) {
                var queryString = path.substr(queryStart + 1, path.length);
                path = path.substr(0, queryStart);
                if (queryString) {
                    queryParams = this.parseQueryString(queryString);
                }
            }

            path = tryDecode(path);
            if (!path) return;

            // DEBUG GROUP path

            if (path.charAt(0) !== "/") {
                path = "/" + path;
            }

            pathLen = path.length;
            if (pathLen > 1 && path.charAt(pathLen - 1) === "/") {
                path = path.substr(0, pathLen - 1);
                isSlashDropped = true;
            }

            for (i = 0, l = path.length; i < l; i++) {
                states = recognizeChar(states, path.charAt(i));
                if (!states.length) {
                    break;
                }
            }

            // END DEBUG GROUP

            var solutions = [];
            for (i = 0, l = states.length; i < l; i++) {
                if (states[i].handlers) {
                    solutions.push(states[i]);
                }
            }

            states = sortSolutions(solutions);

            var state = solutions[0];

            if (state && state.handlers) {
                // if a trailing slash was dropped and a star segment is the last segment
                // specified, put the trailing slash back
                if (isSlashDropped && state.regex.source.slice(-5) === "(.+)$") {
                    path = path + "/";
                }
                return findHandler(state, path, queryParams);
            }
        }
    };

    RouteRecognizer.prototype.map = map;

    var genQuery = RouteRecognizer.prototype.generateQueryString;

    // export default for holding the Vue reference
    var exports$1 = {};
    /**
     * Warn stuff.
     *
     * @param {String} msg
     */

    function warn$1(msg) {
        /* istanbul ignore next */
        if (typeof console !== 'undefined') {
            console.error('[vue-router] ' + msg);
        }
    }

    /**
     * Resolve a relative path.
     *
     * @param {String} base
     * @param {String} relative
     * @param {Boolean} append
     * @return {String}
     */

    function resolvePath(base, relative, append) {
        var query = base.match(/(\?.*)$/);
        if (query) {
            query = query[1];
            base = base.slice(0, -query.length);
        }
        // a query!
        if (relative.charAt(0) === '?') {
            return base + relative;
        }
        var stack = base.split('/');
        // remove trailing segment if:
        // - not appending
        // - appending to trailing slash (last segment is empty)
        if (!append || !stack[stack.length - 1]) {
            stack.pop();
        }
        // resolve relative path
        var segments = relative.replace(/^\//, '').split('/');
        for (var i = 0; i < segments.length; i++) {
            var segment = segments[i];
            if (segment === '.') {
                continue;
            } else if (segment === '..') {
                stack.pop();
            } else {
                stack.push(segment);
            }
        }
        // ensure leading slash
        if (stack[0] !== '') {
            stack.unshift('');
        }
        return stack.join('/');
    }

    /**
     * Forgiving check for a promise
     *
     * @param {Object} p
     * @return {Boolean}
     */

    function isPromise(p) {
        return p && typeof p.then === 'function';
    }

    /**
     * Retrive a route config field from a component instance
     * OR a component contructor.
     *
     * @param {Function|Vue} component
     * @param {String} name
     * @return {*}
     */

    function getRouteConfig(component, name) {
        var options = component && (component.$options || component.options);
        return options && options.route && options.route[name];
    }

    /**
     * Resolve an async component factory. Have to do a dirty
     * mock here because of Vue core's internal API depends on
     * an ID check.
     *
     * @param {Object} handler
     * @param {Function} cb
     */

    var resolver = undefined;

    function resolveAsyncComponent(handler, cb) {
        if (!resolver) {
            resolver = {
                resolve: exports$1.Vue.prototype._resolveComponent,
                $options: {
                    components: {
                        _: handler.component
                    }
                }
            };
        } else {
            resolver.$options.components._ = handler.component;
        }
        resolver.resolve('_', function (Component) {
            handler.component = Component;
            cb(Component);
        });
    }

    /**
     * Map the dynamic segments in a path to params.
     *
     * @param {String} path
     * @param {Object} params
     * @param {Object} query
     */

    function mapParams(path, params, query) {
        if (params === undefined) params = {};

        path = path.replace(/:([^\/]+)/g, function (_, key) {
            var val = params[key];
            /* istanbul ignore if */
            if (!val) {
                warn$1('param "' + key + '" not found when generating ' + 'path for "' + path + '" with params ' + JSON.stringify(params));
            }
            return val || '';
        });
        if (query) {
            path += genQuery(query);
        }
        return path;
    }

    var hashRE = /#.*$/;

    var HTML5History = (function () {
        function HTML5History(_ref) {
            var root = _ref.root;
            var onChange = _ref.onChange;
            babelHelpers.classCallCheck(this, HTML5History);

            if (root && root !== '/') {
                // make sure there's the starting slash
                if (root.charAt(0) !== '/') {
                    root = '/' + root;
                }
                // remove trailing slash
                this.root = root.replace(/\/$/, '');
                this.rootRE = new RegExp('^\\' + this.root);
            } else {
                this.root = null;
            }
            this.onChange = onChange;
            // check base tag
            var baseEl = document.querySelector('base');
            this.base = baseEl && baseEl.getAttribute('href');
        }

        HTML5History.prototype.start = function start() {
            var _this = this;

            this.listener = function (e) {
                var url = location.pathname + location.search;
                if (_this.root) {
                    url = url.replace(_this.rootRE, '');
                }
                _this.onChange(url, e && e.state, location.hash);
            };
            window.addEventListener('popstate', this.listener);
            this.listener();
        };

        HTML5History.prototype.stop = function stop() {
            window.removeEventListener('popstate', this.listener);
        };

        HTML5History.prototype.go = function go(path, replace, append) {
            var url = this.formatPath(path, append);
            if (replace) {
                history.replaceState({}, '', url);
            } else {
                // record scroll position by replacing current state
                history.replaceState({
                    pos: {
                        x: window.pageXOffset,
                        y: window.pageYOffset
                    }
                }, '', location.href);
                // then push new state
                history.pushState({}, '', url);
            }
            var hashMatch = path.match(hashRE);
            var hash = hashMatch && hashMatch[0];
            path = url
            // strip hash so it doesn't mess up params
                .replace(hashRE, '')
                // remove root before matching
                .replace(this.rootRE, '');
            this.onChange(path, null, hash);
        };

        HTML5History.prototype.formatPath = function formatPath(path, append) {
            return path.charAt(0) === '/'
                // absolute path
                ? this.root ? this.root + '/' + path.replace(/^\//, '') : path : resolvePath(this.base || location.pathname, path, append);
        };

        return HTML5History;
    })();

    var HashHistory = (function () {
        function HashHistory(_ref) {
            var hashbang = _ref.hashbang;
            var onChange = _ref.onChange;
            babelHelpers.classCallCheck(this, HashHistory);

            this.hashbang = hashbang;
            this.onChange = onChange;
        }

        HashHistory.prototype.start = function start() {
            var self = this;
            this.listener = function () {
                var path = location.hash;
                var raw = path.replace(/^#!?/, '');
                // always
                if (raw.charAt(0) !== '/') {
                    raw = '/' + raw;
                }
                var formattedPath = self.formatPath(raw);
                if (formattedPath !== path) {
                    location.replace(formattedPath);
                    return;
                }
                // determine query
                // note it's possible to have queries in both the actual URL
                // and the hash fragment itself.
                var query = location.search && path.indexOf('?') > -1 ? '&' + location.search.slice(1) : location.search;
                self.onChange(path.replace(/^#!?/, '') + query);
            };
            window.addEventListener('hashchange', this.listener);
            this.listener();
        };

        HashHistory.prototype.stop = function stop() {
            window.removeEventListener('hashchange', this.listener);
        };

        HashHistory.prototype.go = function go(path, replace, append) {
            path = this.formatPath(path, append);
            if (replace) {
                location.replace(path);
            } else {
                location.hash = path;
            }
        };

        HashHistory.prototype.formatPath = function formatPath(path, append) {
            var isAbsoloute = path.charAt(0) === '/';
            var prefix = '#' + (this.hashbang ? '!' : '');
            return isAbsoloute ? prefix + path : prefix + resolvePath(location.hash.replace(/^#!?/, ''), path, append);
        };

        return HashHistory;
    })();

    var AbstractHistory = (function () {
        function AbstractHistory(_ref) {
            var onChange = _ref.onChange;
            babelHelpers.classCallCheck(this, AbstractHistory);

            this.onChange = onChange;
            this.currentPath = '/';
        }

        AbstractHistory.prototype.start = function start() {
            this.onChange('/');
        };

        AbstractHistory.prototype.stop = function stop() {
            // noop
        };

        AbstractHistory.prototype.go = function go(path, replace, append) {
            path = this.currentPath = this.formatPath(path, append);
            this.onChange(path);
        };

        AbstractHistory.prototype.formatPath = function formatPath(path, append) {
            return path.charAt(0) === '/' ? path : resolvePath(this.currentPath, path, append);
        };

        return AbstractHistory;
    })();

    /**
     * Determine the reusability of an existing router view.
     *
     * @param {Directive} view
     * @param {Object} handler
     * @param {Transition} transition
     */

    function canReuse(view, handler, transition) {
        var component = view.childVM;
        if (!component || !handler) {
            return false;
        }
        // important: check view.Component here because it may
        // have been changed in activate hook
        if (view.Component !== handler.component) {
            return false;
        }
        var canReuseFn = getRouteConfig(component, 'canReuse');
        return typeof canReuseFn === 'boolean' ? canReuseFn : canReuseFn ? canReuseFn.call(component, {
            to: transition.to,
            from: transition.from
        }) : true; // defaults to true
    }

    /**
     * Check if a component can deactivate.
     *
     * @param {Directive} view
     * @param {Transition} transition
     * @param {Function} next
     */

    function canDeactivate(view, transition, next) {
        var fromComponent = view.childVM;
        var hook = getRouteConfig(fromComponent, 'canDeactivate');
        if (!hook) {
            next();
        } else {
            transition.callHook(hook, fromComponent, next, {
                expectBoolean: true
            });
        }
    }

    /**
     * Check if a component can activate.
     *
     * @param {Object} handler
     * @param {Transition} transition
     * @param {Function} next
     */

    function canActivate(handler, transition, next) {
        resolveAsyncComponent(handler, function (Component) {
            // have to check due to async-ness
            if (transition.aborted) {
                return;
            }
            // determine if this component can be activated
            var hook = getRouteConfig(Component, 'canActivate');
            if (!hook) {
                next();
            } else {
                transition.callHook(hook, null, next, {
                    expectBoolean: true
                });
            }
        });
    }

    /**
     * Call deactivate hooks for existing router-views.
     *
     * @param {Directive} view
     * @param {Transition} transition
     * @param {Function} next
     */

    function deactivate(view, transition, next) {
        var component = view.childVM;
        var hook = getRouteConfig(component, 'deactivate');
        if (!hook) {
            next();
        } else {
            transition.callHooks(hook, component, next);
        }
    }

    /**
     * Activate / switch component for a router-view.
     *
     * @param {Directive} view
     * @param {Transition} transition
     * @param {Number} depth
     * @param {Function} [cb]
     */

    function activate(view, transition, depth, cb, reuse) {
        var handler = transition.activateQueue[depth];
        if (!handler) {
            saveChildView(view);
            if (view._bound) {
                view.setComponent(null);
            }
            cb && cb();
            return;
        }

        var Component = view.Component = handler.component;
        var activateHook = getRouteConfig(Component, 'activate');
        var dataHook = getRouteConfig(Component, 'data');
        var waitForData = getRouteConfig(Component, 'waitForData');

        view.depth = depth;
        view.activated = false;

        var component = undefined;
        var loading = !!(dataHook && !waitForData);

        // "reuse" is a flag passed down when the parent view is
        // either reused via keep-alive or as a child of a kept-alive view.
        // of course we can only reuse if the current kept-alive instance
        // is of the correct type.
        reuse = reuse && view.childVM && view.childVM.constructor === Component;

        if (reuse) {
            // just reuse
            component = view.childVM;
            component.$loadingRouteData = loading;
        } else {
            saveChildView(view);

            // unbuild current component. this step also destroys
            // and removes all nested child views.
            view.unbuild(true);

            // build the new component. this will also create the
            // direct child view of the current one. it will register
            // itself as view.childView.
            component = view.build({
                _meta: {
                    $loadingRouteData: loading
                },
                created: function created() {
                    this._routerView = view;
                }
            });

            // handle keep-alive.
            // when a kept-alive child vm is restored, we need to
            // add its cached child views into the router's view list,
            // and also properly update current view's child view.
            if (view.keepAlive) {
                component.$loadingRouteData = loading;
                var cachedChildView = component._keepAliveRouterView;
                if (cachedChildView) {
                    view.childView = cachedChildView;
                    component._keepAliveRouterView = null;
                }
            }
        }

        // cleanup the component in case the transition is aborted
        // before the component is ever inserted.
        var cleanup = function cleanup() {
            component.$destroy();
        };

        // actually insert the component and trigger transition
        var insert = function insert() {
            if (reuse) {
                cb && cb();
                return;
            }
            var router = transition.router;
            if (router._rendered || router._transitionOnLoad) {
                view.transition(component);
            } else {
                // no transition on first render, manual transition
                /* istanbul ignore if */
                if (view.setCurrent) {
                    // 0.12 compat
                    view.setCurrent(component);
                } else {
                    // 1.0
                    view.childVM = component;
                }
                component.$before(view.anchor, null, false);
            }
            cb && cb();
        };

        var afterData = function afterData() {
            // activate the child view
            if (view.childView) {
                activate(view.childView, transition, depth + 1, null, reuse || view.keepAlive);
            }
            insert();
        };

        // called after activation hook is resolved
        var afterActivate = function afterActivate() {
            view.activated = true;
            if (dataHook && waitForData) {
                // wait until data loaded to insert
                loadData(component, transition, dataHook, afterData, cleanup);
            } else {
                // load data and insert at the same time
                if (dataHook) {
                    loadData(component, transition, dataHook);
                }
                afterData();
            }
        };

        if (activateHook) {
            transition.callHooks(activateHook, component, afterActivate, {
                cleanup: cleanup,
                postActivate: true
            });
        } else {
            afterActivate();
        }
    }

    /**
     * Reuse a view, just reload data if necessary.
     *
     * @param {Directive} view
     * @param {Transition} transition
     */

    function reuse(view, transition) {
        var component = view.childVM;
        var dataHook = getRouteConfig(component, 'data');
        if (dataHook) {
            loadData(component, transition, dataHook);
        }
    }

    /**
     * Asynchronously load and apply data to component.
     *
     * @param {Vue} component
     * @param {Transition} transition
     * @param {Function} hook
     * @param {Function} cb
     * @param {Function} cleanup
     */

    function loadData(component, transition, hook, cb, cleanup) {
        component.$loadingRouteData = true;
        transition.callHooks(hook, component, function () {
            component.$loadingRouteData = false;
            component.$emit('route-data-loaded', component);
            cb && cb();
        }, {
            cleanup: cleanup,
            postActivate: true,
            processData: function processData(data) {
                // handle promise sugar syntax
                var promises = [];
                if (isPlainObject(data)) {
                    Object.keys(data).forEach(function (key) {
                        var val = data[key];
                        if (isPromise(val)) {
                            promises.push(val.then(function (resolvedVal) {
                                component.$set(key, resolvedVal);
                            }));
                        } else {
                            component.$set(key, val);
                        }
                    });
                }
                if (promises.length) {
                    return promises[0].constructor.all(promises);
                }
            }
        });
    }

    /**
     * Save the child view for a kept-alive view so that
     * we can restore it when it is switched back to.
     *
     * @param {Directive} view
     */

    function saveChildView(view) {
        if (view.keepAlive && view.childVM && view.childView) {
            view.childVM._keepAliveRouterView = view.childView;
        }
        view.childView = null;
    }

    /**
     * Check plain object.
     *
     * @param {*} val
     */

    function isPlainObject(val) {
        return Object.prototype.toString.call(val) === '[object Object]';
    }

    /**
     * A RouteTransition object manages the pipeline of a
     * router-view switching process. This is also the object
     * passed into user route hooks.
     *
     * @param {Router} router
     * @param {Route} to
     * @param {Route} from
     */

    var RouteTransition = (function () {
        function RouteTransition(router, to, from) {
            babelHelpers.classCallCheck(this, RouteTransition);

            this.router = router;
            this.to = to;
            this.from = from;
            this.next = null;
            this.aborted = false;
            this.done = false;
        }

        /**
         * Abort current transition and return to previous location.
         */

        RouteTransition.prototype.abort = function abort() {
            if (!this.aborted) {
                this.aborted = true;
                // if the root path throws an error during validation
                // on initial load, it gets caught in an infinite loop.
                var abortingOnLoad = !this.from.path && this.to.path === '/';
                if (!abortingOnLoad) {
                    this.router.replace(this.from.path || '/');
                }
            }
        };

        /**
         * Abort current transition and redirect to a new location.
         *
         * @param {String} path
         */

        RouteTransition.prototype.redirect = function redirect(path) {
            if (!this.aborted) {
                this.aborted = true;
                if (typeof path === 'string') {
                    path = mapParams(path, this.to.params, this.to.query);
                } else {
                    path.params = path.params || this.to.params;
                    path.query = path.query || this.to.query;
                }
                this.router.replace(path);
            }
        };

        /**
         * A router view transition's pipeline can be described as
         * follows, assuming we are transitioning from an existing
         * <router-view> chain [Component A, Component B] to a new
         * chain [Component A, Component C]:
         *
         *  A    A
         *  | => |
         *  B    C
         *
         * 1. Reusablity phase:
         *   -> canReuse(A, A)
         *   -> canReuse(B, C)
         *   -> determine new queues:
         *      - deactivation: [B]
         *      - activation: [C]
         *
         * 2. Validation phase:
         *   -> canDeactivate(B)
         *   -> canActivate(C)
         *
         * 3. Activation phase:
         *   -> deactivate(B)
         *   -> activate(C)
         *
         * Each of these steps can be asynchronous, and any
         * step can potentially abort the transition.
         *
         * @param {Function} cb
         */

        RouteTransition.prototype.start = function start(cb) {
            var transition = this;

            // determine the queue of views to deactivate
            var deactivateQueue = [];
            var view = this.router._rootView;
            while (view) {
                deactivateQueue.unshift(view);
                view = view.childView;
            }
            var reverseDeactivateQueue = deactivateQueue.slice().reverse();

            // determine the queue of route handlers to activate
            var activateQueue = this.activateQueue = toArray(this.to.matched).map(function (match) {
                return match.handler;
            });

            // 1. Reusability phase
            var i = undefined,
                reuseQueue = undefined;
            for (i = 0; i < reverseDeactivateQueue.length; i++) {
                if (!canReuse(reverseDeactivateQueue[i], activateQueue[i], transition)) {
                    break;
                }
            }
            if (i > 0) {
                reuseQueue = reverseDeactivateQueue.slice(0, i);
                deactivateQueue = reverseDeactivateQueue.slice(i).reverse();
                activateQueue = activateQueue.slice(i);
            }

            // 2. Validation phase
            transition.runQueue(deactivateQueue, canDeactivate, function () {
                transition.runQueue(activateQueue, canActivate, function () {
                    transition.runQueue(deactivateQueue, deactivate, function () {
                        // 3. Activation phase

                        // Update router current route
                        transition.router._onTransitionValidated(transition);

                        // trigger reuse for all reused views
                        reuseQueue && reuseQueue.forEach(function (view) {
                            return reuse(view, transition);
                        });

                        // the root of the chain that needs to be replaced
                        // is the top-most non-reusable view.
                        if (deactivateQueue.length) {
                            var _view = deactivateQueue[deactivateQueue.length - 1];
                            var depth = reuseQueue ? reuseQueue.length : 0;
                            activate(_view, transition, depth, cb);
                        } else {
                            cb();
                        }
                    });
                });
            });
        };

        /**
         * Asynchronously and sequentially apply a function to a
         * queue.
         *
         * @param {Array} queue
         * @param {Function} fn
         * @param {Function} cb
         */

        RouteTransition.prototype.runQueue = function runQueue(queue, fn, cb) {
            var transition = this;
            step(0);
            function step(index) {
                if (index >= queue.length) {
                    cb();
                } else {
                    fn(queue[index], transition, function () {
                        step(index + 1);
                    });
                }
            }
        };

        /**
         * Call a user provided route transition hook and handle
         * the response (e.g. if the user returns a promise).
         *
         * If the user neither expects an argument nor returns a
         * promise, the hook is assumed to be synchronous.
         *
         * @param {Function} hook
         * @param {*} [context]
         * @param {Function} [cb]
         * @param {Object} [options]
         *                 - {Boolean} expectBoolean
         *                 - {Boolean} postActive
         *                 - {Function} processData
         *                 - {Function} cleanup
         */

        RouteTransition.prototype.callHook = function callHook(hook, context, cb) {
            var _ref = arguments.length <= 3 || arguments[3] === undefined ? {} : arguments[3];

            var _ref$expectBoolean = _ref.expectBoolean;
            var expectBoolean = _ref$expectBoolean === undefined ? false : _ref$expectBoolean;
            var _ref$postActivate = _ref.postActivate;
            var postActivate = _ref$postActivate === undefined ? false : _ref$postActivate;
            var processData = _ref.processData;
            var cleanup = _ref.cleanup;

            var transition = this;
            var nextCalled = false;

            // abort the transition
            var abort = function abort() {
                cleanup && cleanup();
                transition.abort();
            };

            // handle errors
            var onError = function onError(err) {
                postActivate ? next() : abort();
                if (err && !transition.router._suppress) {
                    warn$1('Uncaught error during transition: ');
                    throw err instanceof Error ? err : new Error(err);
                }
            };

            // since promise swallows errors, we have to
            // throw it in the next tick...
            var onPromiseError = function onPromiseError(err) {
                try {
                    onError(err);
                } catch (e) {
                    setTimeout(function () {
                        throw e;
                    }, 0);
                }
            };

            // advance the transition to the next step
            var next = function next() {
                if (nextCalled) {
                    warn$1('transition.next() should be called only once.');
                    return;
                }
                nextCalled = true;
                if (transition.aborted) {
                    cleanup && cleanup();
                    return;
                }
                cb && cb();
            };

            var nextWithBoolean = function nextWithBoolean(res) {
                if (typeof res === 'boolean') {
                    res ? next() : abort();
                } else if (isPromise(res)) {
                    res.then(function (ok) {
                        ok ? next() : abort();
                    }, onPromiseError);
                } else if (!hook.length) {
                    next();
                }
            };

            var nextWithData = function nextWithData(data) {
                var res = undefined;
                try {
                    res = processData(data);
                } catch (err) {
                    return onError(err);
                }
                if (isPromise(res)) {
                    res.then(next, onPromiseError);
                } else {
                    next();
                }
            };

            // expose a clone of the transition object, so that each
            // hook gets a clean copy and prevent the user from
            // messing with the internals.
            var exposed = {
                to: transition.to,
                from: transition.from,
                abort: abort,
                next: processData ? nextWithData : next,
                redirect: function redirect() {
                    transition.redirect.apply(transition, arguments);
                }
            };

            // actually call the hook
            var res = undefined;
            try {
                res = hook.call(context, exposed);
            } catch (err) {
                return onError(err);
            }

            if (expectBoolean) {
                // boolean hooks
                nextWithBoolean(res);
            } else if (isPromise(res)) {
                // promise
                if (processData) {
                    res.then(nextWithData, onPromiseError);
                } else {
                    res.then(next, onPromiseError);
                }
            } else if (processData && isPlainOjbect(res)) {
                // data promise sugar
                nextWithData(res);
            } else if (!hook.length) {
                next();
            }
        };

        /**
         * Call a single hook or an array of async hooks in series.
         *
         * @param {Array} hooks
         * @param {*} context
         * @param {Function} cb
         * @param {Object} [options]
         */

        RouteTransition.prototype.callHooks = function callHooks(hooks, context, cb, options) {
            var _this = this;

            if (Array.isArray(hooks)) {
                this.runQueue(hooks, function (hook, _, next) {
                    if (!_this.aborted) {
                        _this.callHook(hook, context, next, options);
                    }
                }, cb);
            } else {
                this.callHook(hooks, context, cb, options);
            }
        };

        return RouteTransition;
    })();

    function isPlainOjbect(val) {
        return Object.prototype.toString.call(val) === '[object Object]';
    }

    function toArray(val) {
        return val ? Array.prototype.slice.call(val) : [];
    }

    var internalKeysRE = /^(component|subRoutes|fullPath)$/;

    /**
     * Route Context Object
     *
     * @param {String} path
     * @param {Router} router
     */

    var Route = function Route(path, router) {
        var _this = this;

        babelHelpers.classCallCheck(this, Route);

        var matched = router._recognizer.recognize(path);
        if (matched) {
            // copy all custom fields from route configs
            [].forEach.call(matched, function (match) {
                for (var key in match.handler) {
                    if (!internalKeysRE.test(key)) {
                        _this[key] = match.handler[key];
                    }
                }
            });
            // set query and params
            this.query = matched.queryParams;
            this.params = [].reduce.call(matched, function (prev, cur) {
                if (cur.params) {
                    for (var key in cur.params) {
                        prev[key] = cur.params[key];
                    }
                }
                return prev;
            }, {});
        }
        // expose path and router
        this.path = path;
        // for internal use
        this.matched = matched || router._notFoundHandler;
        // internal reference to router
        Object.defineProperty(this, 'router', {
            enumerable: false,
            value: router
        });
        // Important: freeze self to prevent observation
        Object.freeze(this);
    };

    function applyOverride (Vue) {
        var _Vue$util = Vue.util;
        var extend = _Vue$util.extend;
        var isArray = _Vue$util.isArray;
        var defineReactive = _Vue$util.defineReactive;

        // override Vue's init and destroy process to keep track of router instances
        var init = Vue.prototype._init;
        Vue.prototype._init = function (options) {
            options = options || {};
            var root = options._parent || options.parent || this;
            var router = root.$router;
            var route = root.$route;
            if (router) {
                // expose router
                this.$router = router;
                router._children.push(this);
                /* istanbul ignore if */
                if (this._defineMeta) {
                    // 0.12
                    this._defineMeta('$route', route);
                } else {
                    // 1.0
                    defineReactive(this, '$route', route);
                }
            }
            init.call(this, options);
        };

        var destroy = Vue.prototype._destroy;
        Vue.prototype._destroy = function () {
            if (!this._isBeingDestroyed && this.$router) {
                this.$router._children.$remove(this);
            }
            destroy.apply(this, arguments);
        };

        // 1.0 only: enable route mixins
        var strats = Vue.config.optionMergeStrategies;
        var hooksToMergeRE = /^(data|activate|deactivate)$/;

        if (strats) {
            strats.route = function (parentVal, childVal) {
                if (!childVal) return parentVal;
                if (!parentVal) return childVal;
                var ret = {};
                extend(ret, parentVal);
                for (var key in childVal) {
                    var a = ret[key];
                    var b = childVal[key];
                    // for data, activate and deactivate, we need to merge them into
                    // arrays similar to lifecycle hooks.
                    if (a && hooksToMergeRE.test(key)) {
                        ret[key] = (isArray(a) ? a : [a]).concat(b);
                    } else {
                        ret[key] = b;
                    }
                }
                return ret;
            };
        }
    }

    function View (Vue) {

        var _ = Vue.util;
        var componentDef =
            // 0.12
            Vue.directive('_component') ||
                // 1.0
            Vue.internalDirectives.component;
        // <router-view> extends the internal component directive
        var viewDef = _.extend({}, componentDef);

        // with some overrides
        _.extend(viewDef, {

            _isRouterView: true,

            bind: function bind() {
                var route = this.vm.$route;
                /* istanbul ignore if */
                if (!route) {
                    warn$1('<router-view> can only be used inside a ' + 'router-enabled app.');
                    return;
                }
                // force dynamic directive so v-component doesn't
                // attempt to build right now
                this._isDynamicLiteral = true;
                // finally, init by delegating to v-component
                componentDef.bind.call(this);

                // locate the parent view
                var parentView = undefined;
                var parent = this.vm;
                while (parent) {
                    if (parent._routerView) {
                        parentView = parent._routerView;
                        break;
                    }
                    parent = parent.$parent;
                }
                if (parentView) {
                    // register self as a child of the parent view,
                    // instead of activating now. This is so that the
                    // child's activate hook is called after the
                    // parent's has resolved.
                    this.parentView = parentView;
                    parentView.childView = this;
                } else {
                    // this is the root view!
                    var router = route.router;
                    router._rootView = this;
                }

                // handle late-rendered view
                // two possibilities:
                // 1. root view rendered after transition has been
                //    validated;
                // 2. child view rendered after parent view has been
                //    activated.
                var transition = route.router._currentTransition;
                if (!parentView && transition.done || parentView && parentView.activated) {
                    var depth = parentView ? parentView.depth + 1 : 0;
                    activate(this, transition, depth);
                }
            },

            unbind: function unbind() {
                if (this.parentView) {
                    this.parentView.childView = null;
                }
                componentDef.unbind.call(this);
            }
        });

        Vue.elementDirective('router-view', viewDef);
    }

    var trailingSlashRE = /\/$/;
    var regexEscapeRE = /[-.*+?^${}()|[\]\/\\]/g;
    var queryStringRE = /\?.*$/;

    // install v-link, which provides navigation support for
    // HTML5 history mode
    function Link (Vue) {
        var _Vue$util = Vue.util;
        var _bind = _Vue$util.bind;
        var isObject = _Vue$util.isObject;
        var addClass = _Vue$util.addClass;
        var removeClass = _Vue$util.removeClass;

        var onPriority = Vue.directive('on').priority;
        var LINK_UPDATE = '__vue-router-link-update__';

        var activeId = 0;

        Vue.directive('link-active', {
            priority: 9999,
            bind: function bind() {
                var _this = this;

                var id = String(activeId++);
                // collect v-links contained within this element.
                // we need do this here before the parent-child relationship
                // gets messed up by terminal directives (if, for, components)
                var childLinks = this.el.querySelectorAll('[v-link]');
                for (var i = 0, l = childLinks.length; i < l; i++) {
                    var link = childLinks[i];
                    var existingId = link.getAttribute(LINK_UPDATE);
                    var value = existingId ? existingId + ',' + id : id;
                    // leave a mark on the link element which can be persisted
                    // through fragment clones.
                    link.setAttribute(LINK_UPDATE, value);
                }
                this.vm.$on(LINK_UPDATE, this.cb = function (link, path) {
                    if (link.activeIds.indexOf(id) > -1) {
                        link.updateClasses(path, _this.el);
                    }
                });
            },
            unbind: function unbind() {
                this.vm.$off(LINK_UPDATE, this.cb);
            }
        });

        Vue.directive('link', {
            priority: onPriority - 2,

            bind: function bind() {
                var vm = this.vm;
                /* istanbul ignore if */
                if (!vm.$route) {
                    warn$1('v-link can only be used inside a router-enabled app.');
                    return;
                }
                this.router = vm.$route.router;
                // update things when the route changes
                this.unwatch = vm.$watch('$route', _bind(this.onRouteUpdate, this));
                // check v-link-active ids
                var activeIds = this.el.getAttribute(LINK_UPDATE);
                if (activeIds) {
                    this.el.removeAttribute(LINK_UPDATE);
                    this.activeIds = activeIds.split(',');
                }
                // no need to handle click if link expects to be opened
                // in a new window/tab.
                /* istanbul ignore if */
                if (this.el.tagName === 'A' && this.el.getAttribute('target') === '_blank') {
                    return;
                }
                // handle click
                this.handler = _bind(this.onClick, this);
                this.el.addEventListener('click', this.handler);
            },

            update: function update(target) {
                this.target = target;
                if (isObject(target)) {
                    this.append = target.append;
                    this.exact = target.exact;
                    this.prevActiveClass = this.activeClass;
                    this.activeClass = target.activeClass;
                }
                this.onRouteUpdate(this.vm.$route);
            },

            onClick: function onClick(e) {
                // don't redirect with control keys
                /* istanbul ignore if */
                if (e.metaKey || e.ctrlKey || e.shiftKey) return;
                // don't redirect when preventDefault called
                /* istanbul ignore if */
                if (e.defaultPrevented) return;
                // don't redirect on right click
                /* istanbul ignore if */
                if (e.button !== 0) return;

                var target = this.target;
                if (target) {
                    // v-link with expression, just go
                    e.preventDefault();
                    this.router.go(target);
                } else {
                    // no expression, delegate for an <a> inside
                    var el = e.target;
                    while (el.tagName !== 'A' && el !== this.el) {
                        el = el.parentNode;
                    }
                    if (el.tagName === 'A' && sameOrigin(el)) {
                        e.preventDefault();
                        var path = el.pathname;
                        if (this.router.history.root) {
                            path = path.replace(this.router.history.rootRE, '');
                        }
                        this.router.go({
                            path: path,
                            replace: target && target.replace,
                            append: target && target.append
                        });
                    }
                }
            },

            onRouteUpdate: function onRouteUpdate(route) {
                // router.stringifyPath is dependent on current route
                // and needs to be called again whenver route changes.
                var newPath = this.router.stringifyPath(this.target);
                if (this.path !== newPath) {
                    this.path = newPath;
                    this.updateActiveMatch();
                    this.updateHref();
                }
                if (this.activeIds) {
                    this.vm.$emit(LINK_UPDATE, this, route.path);
                } else {
                    this.updateClasses(route.path, this.el);
                }
            },

            updateActiveMatch: function updateActiveMatch() {
                this.activeRE = this.path && !this.exact ? new RegExp('^' + this.path.replace(/\/$/, '').replace(queryStringRE, '').replace(regexEscapeRE, '\\$&') + '(\\/|$)') : null;
            },

            updateHref: function updateHref() {
                if (this.el.tagName !== 'A') {
                    return;
                }
                var path = this.path;
                var router = this.router;
                var isAbsolute = path.charAt(0) === '/';
                // do not format non-hash relative paths
                var href = path && (router.mode === 'hash' || isAbsolute) ? router.history.formatPath(path, this.append) : path;
                if (href) {
                    this.el.href = href;
                } else {
                    this.el.removeAttribute('href');
                }
            },

            updateClasses: function updateClasses(path, el) {
                var activeClass = this.activeClass || this.router._linkActiveClass;
                // clear old class
                if (this.prevActiveClass && this.prevActiveClass !== activeClass) {
                    toggleClasses(el, this.prevActiveClass, removeClass);
                }
                // remove query string before matching
                var dest = this.path.replace(queryStringRE, '');
                path = path.replace(queryStringRE, '');
                // add new class
                if (this.exact) {
                    if (dest === path ||
                            // also allow additional trailing slash
                        dest.charAt(dest.length - 1) !== '/' && dest === path.replace(trailingSlashRE, '')) {
                        toggleClasses(el, activeClass, addClass);
                    } else {
                        toggleClasses(el, activeClass, removeClass);
                    }
                } else {
                    if (this.activeRE && this.activeRE.test(path)) {
                        toggleClasses(el, activeClass, addClass);
                    } else {
                        toggleClasses(el, activeClass, removeClass);
                    }
                }
            },

            unbind: function unbind() {
                this.el.removeEventListener('click', this.handler);
                this.unwatch && this.unwatch();
            }
        });

        function sameOrigin(link) {
            return link.protocol === location.protocol && link.hostname === location.hostname && link.port === location.port;
        }

        // this function is copied from v-bind:class implementation until
        // we properly expose it...
        function toggleClasses(el, key, fn) {
            key = key.trim();
            if (key.indexOf(' ') === -1) {
                fn(el, key);
                return;
            }
            var keys = key.split(/\s+/);
            for (var i = 0, l = keys.length; i < l; i++) {
                fn(el, keys[i]);
            }
        }
    }

    var historyBackends = {
        abstract: AbstractHistory,
        hash: HashHistory,
        html5: HTML5History
    };

    // late bind during install
    var Vue = undefined;

    /**
     * Router constructor
     *
     * @param {Object} [options]
     */

    var Router = (function () {
        function Router() {
            var _this = this;

            var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];

            var _ref$hashbang = _ref.hashbang;
            var hashbang = _ref$hashbang === undefined ? true : _ref$hashbang;
            var _ref$abstract = _ref.abstract;
            var abstract = _ref$abstract === undefined ? false : _ref$abstract;
            var _ref$history = _ref.history;
            var history = _ref$history === undefined ? false : _ref$history;
            var _ref$saveScrollPosition = _ref.saveScrollPosition;
            var saveScrollPosition = _ref$saveScrollPosition === undefined ? false : _ref$saveScrollPosition;
            var _ref$transitionOnLoad = _ref.transitionOnLoad;
            var transitionOnLoad = _ref$transitionOnLoad === undefined ? false : _ref$transitionOnLoad;
            var _ref$suppressTransitionError = _ref.suppressTransitionError;
            var suppressTransitionError = _ref$suppressTransitionError === undefined ? false : _ref$suppressTransitionError;
            var _ref$root = _ref.root;
            var root = _ref$root === undefined ? null : _ref$root;
            var _ref$linkActiveClass = _ref.linkActiveClass;
            var linkActiveClass = _ref$linkActiveClass === undefined ? 'v-link-active' : _ref$linkActiveClass;
            babelHelpers.classCallCheck(this, Router);

            /* istanbul ignore if */
            if (!Router.installed) {
                throw new Error('Please install the Router with Vue.use() before ' + 'creating an instance.');
            }

            // Vue instances
            this.app = null;
            this._children = [];

            // route recognizer
            this._recognizer = new RouteRecognizer();
            this._guardRecognizer = new RouteRecognizer();

            // state
            this._started = false;
            this._startCb = null;
            this._currentRoute = {};
            this._currentTransition = null;
            this._previousTransition = null;
            this._notFoundHandler = null;
            this._notFoundRedirect = null;
            this._beforeEachHooks = [];
            this._afterEachHooks = [];

            // trigger transition on initial render?
            this._rendered = false;
            this._transitionOnLoad = transitionOnLoad;

            // history mode
            this._root = root;
            this._abstract = abstract;
            this._hashbang = hashbang;

            // check if HTML5 history is available
            var hasPushState = typeof window !== 'undefined' && window.history && window.history.pushState;
            this._history = history && hasPushState;
            this._historyFallback = history && !hasPushState;

            // create history object
            var inBrowser = Vue.util.inBrowser;
            this.mode = !inBrowser || this._abstract ? 'abstract' : this._history ? 'html5' : 'hash';

            var History = historyBackends[this.mode];
            this.history = new History({
                root: root,
                hashbang: this._hashbang,
                onChange: function onChange(path, state, anchor) {
                    _this._match(path, state, anchor);
                }
            });

            // other options
            this._saveScrollPosition = saveScrollPosition;
            this._linkActiveClass = linkActiveClass;
            this._suppress = suppressTransitionError;
        }

        /**
         * Allow directly passing components to a route
         * definition.
         *
         * @param {String} path
         * @param {Object} handler
         */

        // API ===================================================

        /**
         * Register a map of top-level paths.
         *
         * @param {Object} map
         */

        Router.prototype.map = function map(_map) {
            for (var route in _map) {
                this.on(route, _map[route]);
            }
            return this;
        };

        /**
         * Register a single root-level path
         *
         * @param {String} rootPath
         * @param {Object} handler
         *                 - {String} component
         *                 - {Object} [subRoutes]
         *                 - {Boolean} [forceRefresh]
         *                 - {Function} [before]
         *                 - {Function} [after]
         */

        Router.prototype.on = function on(rootPath, handler) {
            if (rootPath === '*') {
                this._notFound(handler);
            } else {
                this._addRoute(rootPath, handler, []);
            }
            return this;
        };

        /**
         * Set redirects.
         *
         * @param {Object} map
         */

        Router.prototype.redirect = function redirect(map) {
            for (var path in map) {
                this._addRedirect(path, map[path]);
            }
            return this;
        };

        /**
         * Set aliases.
         *
         * @param {Object} map
         */

        Router.prototype.alias = function alias(map) {
            for (var path in map) {
                this._addAlias(path, map[path]);
            }
            return this;
        };

        /**
         * Set global before hook.
         *
         * @param {Function} fn
         */

        Router.prototype.beforeEach = function beforeEach(fn) {
            this._beforeEachHooks.push(fn);
            return this;
        };

        /**
         * Set global after hook.
         *
         * @param {Function} fn
         */

        Router.prototype.afterEach = function afterEach(fn) {
            this._afterEachHooks.push(fn);
            return this;
        };

        /**
         * Navigate to a given path.
         * The path can be an object describing a named path in
         * the format of { name: '...', params: {}, query: {}}
         * The path is assumed to be already decoded, and will
         * be resolved against root (if provided)
         *
         * @param {String|Object} path
         * @param {Boolean} [replace]
         */

        Router.prototype.go = function go(path) {
            var replace = false;
            var append = false;
            if (Vue.util.isObject(path)) {
                replace = path.replace;
                append = path.append;
            }
            path = this.stringifyPath(path);
            if (path) {
                this.history.go(path, replace, append);
            }
        };

        /**
         * Short hand for replacing current path
         *
         * @param {String} path
         */

        Router.prototype.replace = function replace(path) {
            if (typeof path === 'string') {
                path = { path: path };
            }
            path.replace = true;
            this.go(path);
        };

        /**
         * Start the router.
         *
         * @param {VueConstructor} App
         * @param {String|Element} container
         * @param {Function} [cb]
         */

        Router.prototype.start = function start(App, container, cb) {
            /* istanbul ignore if */
            if (this._started) {
                warn$1('already started.');
                return;
            }
            this._started = true;
            this._startCb = cb;
            if (!this.app) {
                /* istanbul ignore if */
                if (!App || !container) {
                    throw new Error('Must start vue-router with a component and a ' + 'root container.');
                }
                /* istanbul ignore if */
                if (App instanceof Vue) {
                    throw new Error('Must start vue-router with a component, not a ' + 'Vue instance.');
                }
                this._appContainer = container;
                var Ctor = this._appConstructor = typeof App === 'function' ? App : Vue.extend(App);
                // give it a name for better debugging
                Ctor.options.name = Ctor.options.name || 'RouterApp';
            }

            // handle history fallback in browsers that do not
            // support HTML5 history API
            if (this._historyFallback) {
                var _location = window.location;
                var _history = new HTML5History({ root: this._root });
                var path = _history.root ? _location.pathname.replace(_history.rootRE, '') : _location.pathname;
                if (path && path !== '/') {
                    _location.assign((_history.root || '') + '/' + this.history.formatPath(path) + _location.search);
                    return;
                }
            }

            this.history.start();
        };

        /**
         * Stop listening to route changes.
         */

        Router.prototype.stop = function stop() {
            this.history.stop();
            this._started = false;
        };

        /**
         * Normalize named route object / string paths into
         * a string.
         *
         * @param {Object|String|Number} path
         * @return {String}
         */

        Router.prototype.stringifyPath = function stringifyPath(path) {
            var generatedPath = '';
            if (path && typeof path === 'object') {
                if (path.name) {
                    var extend = Vue.util.extend;
                    var currentParams = this._currentTransition && this._currentTransition.to.params;
                    var targetParams = path.params || {};
                    var params = currentParams ? extend(extend({}, currentParams), targetParams) : targetParams;
                    generatedPath = encodeURI(this._recognizer.generate(path.name, params));
                } else if (path.path) {
                    generatedPath = encodeURI(path.path);
                }
                if (path.query) {
                    // note: the generated query string is pre-URL-encoded by the recognizer
                    var query = this._recognizer.generateQueryString(path.query);
                    if (generatedPath.indexOf('?') > -1) {
                        generatedPath += '&' + query.slice(1);
                    } else {
                        generatedPath += query;
                    }
                }
            } else {
                generatedPath = encodeURI(path ? path + '' : '');
            }
            return generatedPath;
        };

        // Internal methods ======================================

        /**
         * Add a route containing a list of segments to the internal
         * route recognizer. Will be called recursively to add all
         * possible sub-routes.
         *
         * @param {String} path
         * @param {Object} handler
         * @param {Array} segments
         */

        Router.prototype._addRoute = function _addRoute(path, handler, segments) {
            guardComponent(path, handler);
            handler.path = path;
            handler.fullPath = (segments.reduce(function (path, segment) {
                return path + segment.path;
            }, '') + path).replace('//', '/');
            segments.push({
                path: path,
                handler: handler
            });
            this._recognizer.add(segments, {
                as: handler.name
            });
            // add sub routes
            if (handler.subRoutes) {
                for (var subPath in handler.subRoutes) {
                    // recursively walk all sub routes
                    this._addRoute(subPath, handler.subRoutes[subPath],
                        // pass a copy in recursion to avoid mutating
                        // across branches
                        segments.slice());
                }
            }
        };

        /**
         * Set the notFound route handler.
         *
         * @param {Object} handler
         */

        Router.prototype._notFound = function _notFound(handler) {
            guardComponent('*', handler);
            this._notFoundHandler = [{ handler: handler }];
        };

        /**
         * Add a redirect record.
         *
         * @param {String} path
         * @param {String} redirectPath
         */

        Router.prototype._addRedirect = function _addRedirect(path, redirectPath) {
            if (path === '*') {
                this._notFoundRedirect = redirectPath;
            } else {
                this._addGuard(path, redirectPath, this.replace);
            }
        };

        /**
         * Add an alias record.
         *
         * @param {String} path
         * @param {String} aliasPath
         */

        Router.prototype._addAlias = function _addAlias(path, aliasPath) {
            this._addGuard(path, aliasPath, this._match);
        };

        /**
         * Add a path guard.
         *
         * @param {String} path
         * @param {String} mappedPath
         * @param {Function} handler
         */

        Router.prototype._addGuard = function _addGuard(path, mappedPath, _handler) {
            var _this2 = this;

            this._guardRecognizer.add([{
                path: path,
                handler: function handler(match, query) {
                    var realPath = mapParams(mappedPath, match.params, query);
                    _handler.call(_this2, realPath);
                }
            }]);
        };

        /**
         * Check if a path matches any redirect records.
         *
         * @param {String} path
         * @return {Boolean} - if true, will skip normal match.
         */

        Router.prototype._checkGuard = function _checkGuard(path) {
            var matched = this._guardRecognizer.recognize(path, true);
            if (matched) {
                matched[0].handler(matched[0], matched.queryParams);
                return true;
            } else if (this._notFoundRedirect) {
                matched = this._recognizer.recognize(path);
                if (!matched) {
                    this.replace(this._notFoundRedirect);
                    return true;
                }
            }
        };

        /**
         * Match a URL path and set the route context on vm,
         * triggering view updates.
         *
         * @param {String} path
         * @param {Object} [state]
         * @param {String} [anchor]
         */

        Router.prototype._match = function _match(path, state, anchor) {
            var _this3 = this;

            if (this._checkGuard(path)) {
                return;
            }

            var currentRoute = this._currentRoute;
            var currentTransition = this._currentTransition;

            if (currentTransition) {
                if (currentTransition.to.path === path) {
                    // do nothing if we have an active transition going to the same path
                    return;
                } else if (currentRoute.path === path) {
                    // We are going to the same path, but we also have an ongoing but
                    // not-yet-validated transition. Abort that transition and reset to
                    // prev transition.
                    currentTransition.aborted = true;
                    this._currentTransition = this._prevTransition;
                    return;
                } else {
                    // going to a totally different path. abort ongoing transition.
                    currentTransition.aborted = true;
                }
            }

            // construct new route and transition context
            var route = new Route(path, this);
            var transition = new RouteTransition(this, route, currentRoute);

            // current transition is updated right now.
            // however, current route will only be updated after the transition has
            // been validated.
            this._prevTransition = currentTransition;
            this._currentTransition = transition;

            if (!this.app) {
                (function () {
                    // initial render
                    var router = _this3;
                    _this3.app = new _this3._appConstructor({
                        el: _this3._appContainer,
                        created: function created() {
                            this.$router = router;
                        },
                        _meta: {
                            $route: route
                        }
                    });
                })();
            }

            // check global before hook
            var beforeHooks = this._beforeEachHooks;
            var startTransition = function startTransition() {
                transition.start(function () {
                    _this3._postTransition(route, state, anchor);
                });
            };

            if (beforeHooks.length) {
                transition.runQueue(beforeHooks, function (hook, _, next) {
                    if (transition === _this3._currentTransition) {
                        transition.callHook(hook, null, next, {
                            expectBoolean: true
                        });
                    }
                }, startTransition);
            } else {
                startTransition();
            }

            if (!this._rendered && this._startCb) {
                this._startCb.call(null);
            }

            // HACK:
            // set rendered to true after the transition start, so
            // that components that are acitvated synchronously know
            // whether it is the initial render.
            this._rendered = true;
        };

        /**
         * Set current to the new transition.
         * This is called by the transition object when the
         * validation of a route has succeeded.
         *
         * @param {Transition} transition
         */

        Router.prototype._onTransitionValidated = function _onTransitionValidated(transition) {
            // set current route
            var route = this._currentRoute = transition.to;
            // update route context for all children
            if (this.app.$route !== route) {
                this.app.$route = route;
                this._children.forEach(function (child) {
                    child.$route = route;
                });
            }
            // call global after hook
            if (this._afterEachHooks.length) {
                this._afterEachHooks.forEach(function (hook) {
                    return hook.call(null, {
                        to: transition.to,
                        from: transition.from
                    });
                });
            }
            this._currentTransition.done = true;
        };

        /**
         * Handle stuff after the transition.
         *
         * @param {Route} route
         * @param {Object} [state]
         * @param {String} [anchor]
         */

        Router.prototype._postTransition = function _postTransition(route, state, anchor) {
            // handle scroll positions
            // saved scroll positions take priority
            // then we check if the path has an anchor
            var pos = state && state.pos;
            if (pos && this._saveScrollPosition) {
                Vue.nextTick(function () {
                    window.scrollTo(pos.x, pos.y);
                });
            } else if (anchor) {
                Vue.nextTick(function () {
                    var el = document.getElementById(anchor.slice(1));
                    if (el) {
                        window.scrollTo(window.scrollX, el.offsetTop);
                    }
                });
            }
        };

        return Router;
    })();

    function guardComponent(path, handler) {
        var comp = handler.component;
        if (Vue.util.isPlainObject(comp)) {
            comp = handler.component = Vue.extend(comp);
        }
        /* istanbul ignore if */
        if (typeof comp !== 'function') {
            handler.component = null;
            warn$1('invalid component for route "' + path + '".');
        }
    }

    /* Installation */

    Router.installed = false;

    /**
     * Installation interface.
     * Install the necessary directives.
     */

    Router.install = function (externalVue) {
        /* istanbul ignore if */
        if (Router.installed) {
            warn$1('already installed.');
            return;
        }
        Vue = externalVue;
        applyOverride(Vue);
        View(Vue);
        Link(Vue);
        exports$1.Vue = Vue;
        Router.installed = true;
    };

    // auto install
    /* istanbul ignore if */
    if (typeof window !== 'undefined' && window.Vue) {
        window.Vue.use(Router);
    }

    return Router;

}));